package com.ccl.iot.managers;

import android.content.Context;
import android.os.SystemClock;

import com.ccl.iot.EIOT_RESULT;
import com.ccl.iot.IOTCallback;
import com.ccl.iot.IOTDataType;
import com.ccl.iot.IOTDataType.IOTShort;
import com.ccl.iot.IOTInterface.IOTAuthMonitorInterface.IOTAuthChangeListener;
import com.ccl.iot.IOTInterface.IOTListMonitorInterface;
import com.ccl.iot.IOTInterface.IOTListMonitorInterface.IOTListChangeListener;
import com.ccl.iot.IOTInterface.IOTOnlineMonitorInterface.IOTOnlineStatusChangeListener;
import com.ccl.iot.IOTNetAPI;
import com.ccl.iot.IOTNetAPIEnum;
import com.ccl.iot.IOTNetAPIEnum.EIOT_NETWORK_TYPE;
import com.ccl.iot.SequenceInvokeList;
import com.ccl.iot.device.IOTDevice;
import com.ccl.iot.device.IOTDeviceConst;
import com.ccl.iot.device.IOTRegisterInterface;
import com.ccl.iot.object.IOTDeviceInfoEx;
import com.ccl.iot.object.IOTRegData;
import com.ccl.iot.object.IOTRegInfo;
import com.ccl.iot.object.IOTRegMonitoring;
import com.ccl.iot.privates.ListMap;
import com.ccl.iot.publics.Debugger;
import com.ccl.iot.publics.UIUtils;

import java.util.ArrayList;
import java.util.Arrays;

public class RegisterManager extends Manager implements IOTRegisterInterface {
    private static RegisterManager gDefault = null;

    public static RegisterManager GetDefaultManager(Context aContext) {
        if (gDefault == null) {
            gDefault = new RegisterManager(aContext);
        }

        return gDefault;
    }

    public static RegisterManager GetDefaultManager() {
        return gDefault;
    }

    /*
     * 	Non-static functions
     */
    private IOTNetAPI iNetAPI = null;
    private UserManager iUserManager = null;
    private DeviceManager iDeviceManager = null;
    //    private ListMap<Long, IOTRegData> iRegisters = null;

    private RegisterManager(Context aContext) {
        super(aContext);
        onCreate();
    }

    @Override
    public boolean filteError(int aError) {
        if (aError == EIOT_RESULT.EIOT_RESULT_NO_EFFECT) {
            //            return iMonitorBuffer.isInvoking();
            return true;
        } else if (aError == EIOT_RESULT.EIOT_RESULT_GENERAL_REGISTER_NO_CHANGE) {
            return iWriteBuffer.isInvoking();
        } else if (aError == EIOT_RESULT.EIOT_RESULT_GENERAL_DEVICE_OFFLINE || EIOT_RESULT.IOT_PERMISSION_ERROR(aError)) {
            return iReadBuffer.isInvoking() || iWriteBuffer.isInvoking()/* || iMonitorBuffer.isInvoking()*/;
        }

        return false;
    }

    private void onCreate() {
        super.create();

        iNetAPI = IOTNetAPI.GetInstance();
        iNetAPI.setRegDataCallback(iRegDataCallback);

        iUserManager = UserManager.GetDefaultManager();
        if (iUserManager != null) {
            iUserManager.addUserEventListener(iUserEventListener);
        }
        iDeviceManager = DeviceManager.GetDefaultManager(UIUtils.getContext());
        iDeviceManager.addListChangeListener(iDeviceChangeListener);
    }

    @Override
    public void onResume() {
        super.onResume();
        ArrayList<IOTDevice> tDevices = iDeviceManager.getList();

        if (tDevices != null) {
            for (int i = 0, tCount = tDevices.size(); i < tCount; i++) {
                newDevice(tDevices.get(i));
            }
        }
    }

    @Override
    public void onSuspend() {
        super.onSuspend();

        //        unMonitorAllRegister();

        iReadBuffer.clear();
        iWriteBuffer.clear();
    }

    private UserManager.UserEventListener iUserEventListener = new UserManager.UserEventListener() {
        @Override
        public void onLoginEvent(boolean aCanceled) {
        }

        @Override
        public void onPrepareLogout() {
            //            unMonitorAllRegister();
        }

        @Override
        public void onLogout(boolean aOK) {
        }
    };

    public void newDevice(IOTDevice aDevice) {
        if (aDevice != null) {
            //            long tID = aDevice.getID();
            //            IOTDeviceInfoEx tInfo = aDevice.getDeviceInfo();
            //
            //            if (tInfo != null) {
            //                if (iRegisters == null) {
            //                    iRegisters = new ListMap<Long, IOTRegData>();
            //                } else if (iRegisters.containsKey(tID)) {
            //                    return;
            //                }
            //
            //                if (iRegisters.add(tID, new IOTRegData(tID, 0, (int) tInfo.iRegisterSpace, new byte[(int) tInfo.iRegisterSpace]))) {
            aDevice.addOnlineChangeListener(iOnlineStatusChangeListener);
            aDevice.addAuthChangeListener(iAuthChangeListener);
            //                    monitorRegister(aDevice.isReadable() == false, tID, aDevice.getRegisterAddress(), aDevice.getRegisterSize());

            readRegister(aDevice);
            //                }
            //            }
        }
    }

    private IOTListChangeListener<IOTDevice> iDeviceChangeListener = new IOTListChangeListener<IOTDevice>() {
        @Override
        public void onNewObject(IOTListMonitorInterface<IOTDevice> aSource, IOTDevice aObject) {
            newDevice(aObject);
        }

        @Override
        public void onObjectRemoved(IOTListMonitorInterface<IOTDevice> aSource, IOTDevice aObject) {
        }
    };

    private void afterDeviceChange(Object aDevice) {
        if (aDevice != null && aDevice instanceof IOTDevice) {
            //            if(iRegisters != null){
            IOTDevice tDevice = (IOTDevice) aDevice;
            //            long tID = tDevice.getID();
            //
            //            if (iRegisters.containsKey(tID)) {
            //                monitorRegister(tDevice.isReadable() == false, tID, tDevice.getRegisterAddress(), tDevice.getRegisterSize());
            readRegister(tDevice);
            //            }
            //            }
        }
    }

    private IOTOnlineStatusChangeListener iOnlineStatusChangeListener = new IOTOnlineStatusChangeListener() {
        @Override
        public void onOnlineStatusChanged(IOTDevice aSource, boolean aOnline) {
            if (aOnline) {
                afterDeviceChange(aSource);
            }
        }
    };

    private IOTAuthChangeListener iAuthChangeListener = new IOTAuthChangeListener() {
        @Override
        public void onAuthChanged(IOTDevice aSource, int aNewAuth) {
            afterDeviceChange(aSource);
        }
    };

    private IOTCallback.RegDataCallback iRegDataCallback = new IOTCallback.RegDataCallback() {

        @Override
        public void onRegDataCallback(int aNetWork, long aDevID, short aAddress, short aSize, byte[] aData, long time) {
//            if (iRegisters != null) {
//                IOTRegData tData = iRegisters.getValue(aDevID);
//                if (tData != null) {
                    IOTDevice tDevice;
                    if ((tDevice = iDeviceManager.findDevice(aDevID)) != null) {
//                        if (tData.replaceData(aData, aAddress, aSize) || tDevice.getDeviceInfo().iType == IOTDeviceConst.TDEVICE_TYPE.EDEVICE_TYPE_TERMINAL_MULTIFUNC_5525) {
                            // Update register data to each device
                            Debugger.Debug("RegData[" + (aNetWork == EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_LOCAL ? "LOCAL" : "REMOTE") + "]:" + aDevID + ", " + aAddress + ", " + aSize + ", " + Arrays.toString(Arrays.copyOf(aData, aSize)) + ", time: " + time);
                            if (tDevice.isOnline() == false) {
                                iDeviceManager.setOnline(tDevice, true);
                            }

                            if (aNetWork == EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_LOCAL) {
                                tDevice.setNetWorkState(EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_LOCAL);
                            }
                            tDevice.updateRegister(aAddress, aSize, aData, 0, aNetWork, time);

//                        }
//                    }
//                }
            }
        }
    };

    /**
     * Read register
     */
    @Override
    public void readRegister(long aDevID, int aAddress, int aSize) {
        iReadBuffer.addItem(new IOTRegInfo(aDevID, aAddress, aSize));
    }

    @Override
    public void readRegister(IOTDevice aDevice) {
        if (aDevice != null) {
            if (aDevice.isReadable() && aDevice.getRegisterSize() > 0) {
                readRegister(aDevice.getID(), aDevice.getRegisterAddress(), aDevice.getRegisterSize());
            }
        }
    }

    private SequenceInvokeList<IOTRegInfo> iReadBuffer = new SequenceInvokeList<IOTRegInfo>("Read register") {
        @Override
        public boolean isValidItem(IOTRegInfo aItem) {
            return aItem != null && aItem.isValid();
        }

        @Override
        public boolean isSameItem(IOTRegInfo aItem1, IOTRegInfo aItem2) {
            if (aItem1 != aItem2) {
                if (isValidItem(aItem1) && isValidItem(aItem2)) {
                    if (isInvoking(aItem1) == false) {
                        if (aItem1.iDevID == aItem2.iDevID) {
                            aItem2.join(aItem1.iAddress, aItem1.iSize);

                            return true;
                        }
                    }
                }
            } else {
                return true;
            }

            return false;
        }

        @Override
        public boolean onInvoke(IOTRegInfo aItem) {
            if (aItem != null) {
                long tTick = SystemClock.elapsedRealtime();

                byte tData[] = new byte[IOTNetAPI.KIOT_DEVICE_REGISTER_PAGE_SIZE];
                IOTShort tSize = new IOTShort((short) tData.length);

                while (!iNetAPI.isStarted()) {
                    if (SystemClock.elapsedRealtime() - tTick > 3000) {
                        return false;
                    }
                }

                if (iNetAPI.readReg(aItem.iDevID, IOTDataType.C16(aItem.iAddress), tSize, tData)) {
                    iRegDataCallback.onRegDataCallback(EIOT_NETWORK_TYPE.EIOT_NETWORK_TYPE_REMOTE, aItem.iDevID, IOTDataType.C16(aItem.iAddress), tSize.getValue(), tData, 0);
                }
            }

            return true;
        }
    };

    /**
     * Write register
     */

    //================modify start=================
    //Add a function
    @Override
    public void writeRegister(final IOTDevice dev, int aAddress, int aSize, byte[] aData, int netWorkState) {
        if (dev.isWritable() || dev.getAuth() == IOTNetAPIEnum.EAUTH_STATE.EAUTH_STATE_FREE) {
            if (iNetAPI.isStarted()) {
                iWriteBuffer.addItem(new IOTRegData(dev.getID(), aAddress, aSize, aData, netWorkState));
            }
        }
    }
    //===============modify end====================

    @Override
    public void writeRegister(long aDevID, int aAddress, int aSize, byte[] aData, int netWorkState) {
        if (iNetAPI.isStarted()) {
            iWriteBuffer.addItem(new IOTRegData(aDevID, aAddress, aSize, aData, netWorkState));
        }
    }

    @Override
    public void writeRegister(IOTDevice aDevice, byte[] aData) {
        writeRegister(aDevice, aData, -1);
    }

    @Override
    public void writeRegister(IOTDevice aDevice, byte[] aData, int netWorkState) {
        if (aDevice != null && aData != null && aData.length == aDevice.getRegisterSize()) {
            //            if (aDevice.isWritable()) {
            //                writeRegister(aDevice.getID(), aDevice.getRegisterAddress(), aDevice.getRegisterSize(), aData, netWorkState);
            //            }

            //===========modify start===========
            writeRegister(aDevice, aDevice.getRegisterAddress(), aDevice.getRegisterSize(), aData, netWorkState);
            //===========modify end===========
        }
    }

    private SequenceInvokeList<IOTRegData> iWriteBuffer = new SequenceInvokeList<IOTRegData>("Write register") {
        @Override
        public boolean isValidItem(IOTRegData aItem) {
            return aItem != null && aItem.isValid();
        }

        @Override
        public boolean isSameItem(IOTRegData aItem1, IOTRegData aItem2) {
            if (aItem1 != aItem2) {
                if (isValidItem(aItem1) && isValidItem(aItem2)) {
                    if (isInvoking(aItem1) == false) {
                        if (aItem1.iDevID == aItem2.iDevID) {
                            return aItem2.expand(aItem1.iAddress, aItem1.iSize, aItem1.iData);
                        }
                    }
                }
            } else {
                return true;
            }

            return false;
        }

        @Override
        public boolean onInvoke(IOTRegData aItem) {
            if (aItem != null) {
                if (aItem.iNetWorkState != -1) {
                    iNetAPI.writeReg(aItem.iDevID, IOTDataType.C16(aItem.iAddress), IOTDataType.C16(aItem.iSize), aItem.iData, aItem.iNetWorkState);
                } else {
                    iNetAPI.writeReg(aItem.iDevID, IOTDataType.C16(aItem.iAddress), IOTDataType.C16(aItem.iSize), aItem.iData);
                }
            }

            return true;
        }
    };

    /**
     * Monitor register
     */
    private ListMap<Long, IOTRegMonitoring> iMonitoringRegister = null;

    /*public void unMonitorAllRegister() {
        iMonitorBuffer.clear();

        if (iMonitoringRegister != null) {
            IOTRegMonitoring tMonitoring;

            for (int i = 0, tCount = iMonitoringRegister.size(); i < tCount; i++) {
                if ((tMonitoring = iMonitoringRegister.getValueAt(i)) != null) {
                    if (tMonitoring.isMonitorable()) {
                        tMonitoring.reset();

                        iNetAPI.monitorReg(tMonitoring);
                    }
                }
            }

            iMonitoringRegister.clear();
        }
    }*/

    /*@Override
    public void monitorRegister(boolean aCancel, long aDevID, int aAddress, int aSize) {
        IOTRegMonitoring tOld = null;
        IOTRegMonitoring tNew = new IOTRegMonitoring(aDevID);

        IOTNetAPI.CalMonitorRegFlag(IOTDataType.C16(aAddress), IOTDataType.C16(aSize), tNew);

        if (aCancel == false) {
            if (iMonitoringRegister == null) {
                iMonitoringRegister = new ListMap<Long, IOTRegMonitoring>();

                if (iMonitoringRegister.add(aDevID, tNew) == false) {
                    return;
                }
            } else if ((tOld = iMonitoringRegister.getValue(aDevID)) != null) {
                if (tOld.compare(tNew) != 0) {
                    if (tOld.join(tNew)) {
                        tNew = tOld;
                    } else {
                        return;
                    }
                } else {
                    return;
                }
            } else {
                if (iMonitoringRegister.add(aDevID, tNew) == false) {
                    return;
                }
            }
            iMonitorBuffer.addItem(tNew);
        } else {
            if (iMonitoringRegister != null) {
                if ((tOld = iMonitoringRegister.getValue(aDevID)) != null) {
                    if (!tOld.separate(tNew)) {
                        return;
                    }
                } else {
                    return;
                }
            }
            iMonitorBuffer.addItem(tOld);
        }
    }*/

    /*public void setMonitorable(boolean aMonitorable) {
        iMonitorBuffer.setInvokable(aMonitorable);
    }*/

    /*private SequenceInvokeList<IOTRegMonitoring> iMonitorBuffer = new SequenceInvokeList<IOTRegMonitoring>("Monitor register") {
        @Override
        public boolean onInvoke(IOTRegMonitoring aItem) {
            if (aItem != null) {
                if (iNetAPI.monitorReg(aItem) == false) {
                    if (iMonitoringRegister != null) {
                        iMonitoringRegister.remove(aItem.iDevID);
                    }
                }
            }

            return true;
        }
    };*/
}
